# coding: UTF-8
'''
Created on 2011/05/04

@author: yang_wanjun
'''
from django.db import models
from django.contrib.contenttypes import generic
from django.contrib.contenttypes.models import ContentType
from django.utils.encoding import force_unicode
from django.utils.translation import gettext_lazy as _

FORCE_LOWERCASE_TAGS = True

class TagManager(models.Manager):
    def update_tags(self, obj, tag_names):
        ctype = ContentType.objects.get_for_model(obj)
        current_tags = list(self.filter(items__content_type__pk=ctype.pk, items__object_pk=obj.pk))
        
        updating_tag_names = parse_tag_input(tag_names)
        if FORCE_LOWERCASE_TAGS:
            updating_tag_names = [t.lower() for t in updating_tag_names]
        
        # Remove tags which no longer apply
        removing_tags = [tag for tag in current_tags if tag.name not in updating_tag_names]
        if len(removing_tags):
            TaggedItem.objects.filter(content_type__pk=ctype.pk,
                                      object_pk=obj.pk,
                                      tag__in=removing_tags).delete()
#        # Add new tags
        current_tag_names = [tag.name for tag in current_tags]
        for tag_name in updating_tag_names:
            if tag_name not in current_tag_names:
                tag, created = self.get_or_create(name=tag_name)
                if created:
                    pass
                taggeditem = TaggedItem(tag=tag, content_type=ctype, object_pk=obj.pk)
                taggeditem.save()
        
def parse_tag_input(input):
    """
    Parses tag input, with multiple word input being activated and
    delineated by commas and double quotes. Quotes take precedence, so
    they may contain commas.

    Returns a sorted list of unique tag names.
    """
    if not input:
        return []

    input = force_unicode(input)

    # Special case - if there are no commas or double quotes in the
    # input, we don't *do* a recall... I mean, we know we only need to
    # split on spaces.
    if u',' not in input and u'"' not in input:
        words = list(set(split_strip(input, u' ')))
        words.sort()
        return words

    words = []
    buffer = []
    # Defer splitting of non-quoted sections until we know if there are
    # any unquoted commas.
    to_be_split = []
    saw_loose_comma = False
    open_quote = False
    i = iter(input)
    try:
        while 1:
            c = i.next()
            if c == u'"':
                if buffer:
                    to_be_split.append(u''.join(buffer))
                    buffer = []
                # Find the matching quote
                open_quote = True
                c = i.next()
                while c != u'"':
                    buffer.append(c)
                    c = i.next()
                if buffer:
                    word = u''.join(buffer).strip()
                    if word:
                        words.append(word)
                    buffer = []
                open_quote = False
            else:
                if not saw_loose_comma and c == u',':
                    saw_loose_comma = True
                buffer.append(c)
    except StopIteration:
        # If we were parsing an open quote which was never closed treat
        # the buffer as unquoted.
        if buffer:
            if open_quote and u',' in buffer:
                saw_loose_comma = True
            to_be_split.append(u''.join(buffer))
    if to_be_split:
        if saw_loose_comma:
            delimiter = u','
        else:
            delimiter = u' '
        for chunk in to_be_split:
            words.extend(split_strip(chunk, delimiter))
    words = list(set(words))
    words.sort()
    return words

def split_strip(input, delimiter=u','):
    """
    Splits ``input`` on ``delimiter``, stripping each resulting string
    and returning a list of non-empty strings.
    """
    if not input:
        return []

    words = [w.strip() for w in input.split(delimiter)]
    return [w for w in words if w]

class Tag(models.Model):
    name = models.CharField(_('name'), max_length=30, unique=True, db_index=True)
    
    objects = TagManager()
    
    class Meta:
        ordering = ('name',)
        
    def __unicode__(self):
        return self.name
    
    def get_article_count(self):
        return self.items.count()
    
class TaggedItem(models.Model):
    tag = models.ForeignKey(Tag, related_name='items')
    content_type = models.ForeignKey(ContentType, verbose_name=_('content type'))
    object_pk = models.PositiveIntegerField(_('object id'), db_index=True)
    content_object = generic.GenericForeignKey(ct_field="content_type", fk_field="object_pk")
    
    class Meta:
        unique_together = (('tag', 'content_type', 'object_pk'),)
    
    def __unicode__(self):
        return u'%s [%s]' % (self.content_object, self.tag)
